﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.GamerServices;

namespace Sunspot
{
    public class ComponentEnvironment
    {
        private List<Component> components = new List<Component>();
        private Component currentHoveringComponent;
        /// <summary>
        /// Sends a component infront all of the others. Returns a boolean indicating
        /// if the a component with the specified handle exists.
        /// </summary>
        /// <param name="componentHandle">The handle of the target component.</param>
        private bool ComponentToFront(int componentHandle)
        {
            var component = GetComponent(componentHandle);
            if (component == null)
                return false;
            else
            {
                components.Remove(component);
                components.Add(component);
                return true;
            }
        }
        /// <summary>
        /// Sends a component behind all of the others. Returns a boolean indicating
        /// if the a component with the specified handle exists.
        /// </summary>
        /// <param name="componentHandle">The handle of the target component.</param>
        private bool ComponentToBack(int componentHandle)
        {
            var component = GetComponent(componentHandle);
            if (component == null)
                return false;
            else
            {
                components.Remove(component);
                components.Insert(0, component);
                return true;
            }
        }
        public bool KillComponent(int componentHandle)
        {
            var component = GetComponent(componentHandle);
            if (component == null)
                return false;
            else
            {
                int componentIndex = components.IndexOf(component);
                components[componentIndex] = null;
                return true;
            }
        }
        public int CreateComponent(Component toAdd)
        {
            int freeHandle = components.IndexOf(null);
            if (freeHandle == -1)
            {
                toAdd.Handle = components.Count;
                components.Add(toAdd);
                return components.Count - 1;
            }
            else
            {
                toAdd.Handle = freeHandle;
                components.Add(toAdd);
                return freeHandle;
            }
        }
        /// <summary>
        /// Returns the component that owns the specified handle, or null if no
        /// component owns the handle.
        /// </summary>
        /// <param name="componentHandle">The handle of the target component.</param>
        public Component GetComponent(int componentHandle)
        {
            if (componentHandle < components.Count)
                return components.Find(component => component.Handle == componentHandle);
            else
                return null;
        }
        public void UpdateComponents(GameTime gameTime, KeyboardState keyboardState, MouseState mouseState)
        {
            UpdateHoveredComponent(mouseState);
            for (int i = 0; i < components.Count; i++)
            {
                components[i].Update(gameTime, keyboardState, mouseState);
            }
        }
        public void DrawComponents(GameTime gameTime, SpriteBatch spriteBatch)
        {
            for (int i = 0; i < components.Count; i++)
            {
                components[i].Draw(gameTime, spriteBatch, components[i].Style);
            }
        }
        /// <summary>
        /// Gets the child of the initialComponent that is under the cursor. Returns the initial
        /// component if no child is under the cursor.
        /// </summary>
        /// <param name="cursorBounds">The location and size of the cursor.</param>
        /// <param name="initialOwner">The component whose children are being checked.</param>
        private Component GetHoveredChild(Rectangle cursorBounds, Component initialOwner)
        {
            // Replacement for a nasty recursive algorithm to find the component under the cursor.
            var currentComponent = initialOwner;
            bool childIntersects = true;
            while (childIntersects)
            {
                childIntersects = false;
                for (int i = currentComponent.Children.Count - 1; i >= 0; i--)
                {
                    if (currentComponent.Children[i].Style.Bounds.Intersects(cursorBounds))
                    {
                        currentComponent = currentComponent.Children[i];
                        childIntersects = true;
                        break;
                    }
                }
            }
            return currentComponent;
        }
        /// <summary>
        /// Updates which control the mouse is hovering over.
        /// </summary>
        /// <param name="mouseState">The current mouse state.</param>
        private void UpdateHoveredComponent(MouseState mouseState)
        {
            // TODO: Refactor this
            Rectangle cursorBounds = new Rectangle(mouseState.X, mouseState.Y, 1, 1);
            if (currentHoveringComponent != null && currentHoveringComponent is IMouseResponder &&
                !currentHoveringComponent.Style.Bounds.Intersects(cursorBounds))
            {
                (currentHoveringComponent as IMouseResponder).InvokeMouseLeave(mouseState);
                currentHoveringComponent = null;
            }
            for (int i = components.Count - 1; i >= 0; i--)
            {
                if (!components[i].Style.Bounds.Intersects(cursorBounds))
                    continue;
                if (mouseState.LeftButton == ButtonState.Pressed ||
                    mouseState.RightButton == ButtonState.Pressed)
                    ComponentToFront(components[i].Handle);
                var hoveringComponent = GetHoveredChild(cursorBounds, components[i]);
                if (!(hoveringComponent is IMouseResponder))
                    continue;
                if (currentHoveringComponent != null && currentHoveringComponent != hoveringComponent)
                {
                    (currentHoveringComponent as IMouseResponder).InvokeMouseLeave(mouseState);
                    currentHoveringComponent = null;
                }
                if (currentHoveringComponent != hoveringComponent)
                    (hoveringComponent as IMouseResponder).InvokeMouseEnter(mouseState);
                currentHoveringComponent = hoveringComponent;
                break;
            }
        }
    }
}
